﻿using System;
using System.Collections.Generic;
using Deer;
using Deer.Collections;
using Deer.Extension;
using Deer.Log;
using Telex.Timer;
using Letter.IO.Channels;

namespace Telex;

sealed partial class RpcChannel : IRpcChannel, IReleasable
{
    private const int UID_SIZE = 16;
    private const int FIXED_MESSAGE_PACK_SIZE = UID_SIZE + sizeof(uint) + sizeof(int);
    private const int FIXED_VERIFY_PACK_SIZE = sizeof(uint) + sizeof(byte);
    private const int FIXED_PAK_SIZE = sizeof(byte) + sizeof(uint);
    private const int TIMEOUT = 20;
    
    public RpcChannel(RpcHost localHost, RpcHost remoteHost, ILogger logger, ProtocolMap protocolMap)
    {
        id = StringIdGenerator.GetNextId();
        this.localHost = localHost;
        this.remoteHost = remoteHost;
        this.logger = logger;
        this.register = new ProtocolRegister(protocolMap);

        this.rcvChannel = ChannelProvider.CreateDataChannel<SocketPak>(OnMessageChannelNotify);
        
        this.responseUnits = new ConcurrentMap<Guid, ResponseUnit>();
        this.waitVerifyUnits = new ConcurrentMap<uint, VerifyUnit>();
        SingleTimer.instance.AddListener(OnTimerEvent);
    }

    private string id;
    private RpcHost localHost;
    private RpcHost remoteHost;
    private ILogger logger;
    private ProtocolRegister register;
    
    private IChannel<SocketPak> rcvChannel;
    private ConcurrentMap<Guid, ResponseUnit> responseUnits;
    private ConcurrentMap<uint, VerifyUnit> waitVerifyUnits;
    private List<RpcPipeline> pipelines = new();
    
    public RpcHost LocalHost
    {
        get { return this.localHost; }
    }

    public RpcHost RemoteHost
    {
        get { return this.remoteHost; }
    }

    public int PipelineCount
    {
        get { return this.pipelines.Count; }
    }

    public bool Active
    {
        get { return this.pipelines.Count > 0; }
    }

    public IProtocolRegister Register
    {
        get { return this.register; }
    }

    private IChannelDataReader<SocketPak> RcvChannelReader
    {
        get { return this.rcvChannel.Reader.ToChannelDataReader(); }
    }

    private IChannelDataWriter<SocketPak> RcvChannelWriter
    {
        get { return this.rcvChannel.Writer.ToChannelDataWriter(); }
    }


    public void AddPipeline(RpcPipeline pipeline)
    {
        if (pipeline == null)
        {
            return;
        }

        pipeline.AddPipelineMessageListener(this.OnRcvPipelineMessage);
        this.pipelines.Add(pipeline);
    }

    public void DelPipeline(string id)
    {
        RpcPipeline pipeline = null;
        foreach (var pipe in pipelines)
        {
            if (id == pipe.Id)
            {
                pipeline = pipe;
                break;
            }
        }
        
        if (pipeline == null)
        {
            return;
        }
        
        pipeline.DelPipelineMessageListener(this.OnRcvPipelineMessage);
        this.pipelines.Remove(pipeline);
    }

    public void Start()
    {
        this.RcvChannelReader.AsyncRcv();
    }
    
    public void Release()
    {
        this.RcvChannelWriter.Complete();
        foreach (var pipeline in this.pipelines)
        {
            pipeline.DisposeAsync().Sync();
        }
        this.pipelines.Clear();
        
        SingleTimer.instance.DelListener(OnTimerEvent);
    }

    struct ResponseUnit
    {
        public Guid uid;
        public long timestamp;
        public CallResponseDelegate func;
    }

    struct VerifyUnit
    {
        public long timestamp;
        public SocketPak pak;
    }
}